package nut.system;
import static org.apache.commons.lang.StringEscapeUtils.escapeHtml;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.util.Date;
import java.util.Map;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.Doc;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.Tag;
import com.sun.tools.doclets.standard.Standard;
/**
 * Doclet that indexes files for nut.
 * 
 * @category beta
 * @author Mark Farnsworth
 */
public class NutDoclet extends Standard implements NutHtml {
	private final static DateFormat dateFormat = NutFactory.the.dateFormat;
	private static PrintWriter out;
	private static String codeHtml(final String text, final String a,
			final String b) {
		return text.replaceAll(escapeHtml(a), b);
	}
	private static boolean doStart(final RootDoc doc)
			throws ClassNotFoundException, IOException, InstantiationException,
			IllegalAccessException, SecurityException,
			IllegalArgumentException, NoSuchMethodException,
			InvocationTargetException {
		scan(doc);
		final FileOutputStream os = new FileOutputStream("nuts.html");
		try {
			out = new PrintWriter(os);
			try {
				final String h = $html;
				s(h);
				s($head);
				s($title);
				s("Nut Report");
				s(title$);
				s($style);
				injectStyle();
				s(style$);
				s($script);
				injectScript();
				s(script$);
				s(head$);
				s("<body>");
				s($$divId + "nutBody" + $);
				s(div$);
				s($$divClz + "NutRaw" + $);
				sStatus();
				sBags();
				sNuts();
				sRoots();
				sFarmers();
				s(div$);
				s(body$);
				s(html$);
			} finally {
				out.close();
			}
		} finally {
			os.close();
		}
		return true;
	}
	private static void injectFile(final String scriptName) throws IOException {
		final InputStream is = NutDoclet.class.getResourceAsStream(scriptName);
		try {
			final Reader isr = new InputStreamReader(is);
			try {
				final BufferedReader br = new BufferedReader(isr);
				try {
					String line;
					while ((line = br.readLine()) != null)
						s(line);
				} finally {
					br.close();
				}
			} finally {
				isr.close();
			}
		} finally {
			is.close();
		}
	}
	private static void injectScript() throws IOException {
		injectFile("/nut/resource/jquery.js.in");
		injectFile("/nut/resource/jquery.ui.js.in");
		injectFile("/nut/resource/jquery.tablesorter.js.in");
		injectFile("/nut/resource/getElementsByClassName-1.0.1.js.in");
		injectFile("/nut/resource/nut.js");
	}
	private static void injectStyle() throws IOException {
		injectFile("/nut/resource/nut.css");
		injectFile("/nut/resource/table-sorter.css");
	}
	private static void onAuthorTag(final Tag item) {
		NutFactory.the.farmer(item.text().trim());
	}
	private static void s(final NutAuthor item) {
		s($$divId + "nut" + item.id + $andClass + "NutFarmer" + $);
		t(item.name);
		s(div$);
	}
	private static void s(final NutCategory n) {
		s($$divId + "nut" + n.id + $andClass + "NutStatus" + $);
		s(n.name);
		s(div$);
	}
	private static void s(final NutItem nut) {
		s($$divId + "nut" + nut.id + $andClass + "Nut" + $);
		s($div);
		t(nut.id);
		s(div$div);
		t(nut.name);
		s(div$div);
		final Doc doc = nut.doc;
		if (doc != null) {
			sTags(doc.inlineTags());
			sTags(doc.tags());
		}
		s(div$div);
		for (final NutCategory item : nut.categories) {
			s($div);
			t(item.id);
			s(div$);
		}
		s(div$div);
		for (final NutAuthor item : nut.authors) {
			s($div);
			t(item.id);
			s(div$);
		}
		s(div$div);
		for (final NutItem item : nut.nuts) {
			s($div);
			t(item.id);
			s(div$);
		}
		s(div$div);
		for (final NutVersion bag : nut.bags) {
			t(bag.id);
			break;
		}
		s(div$);
		s(div$);
	}
	private static void s(final NutVersion bag) {
		s($$divId + "nut" + bag.id + $andClass + "NutBag" + $);
		s($div);
		s(bag.name);
		s(div$div);
		for (final Date date : bag.target.values()) {
			s($div);
			s(dateFormat.format(date));
			s(div$);
		}
		s(div$);
		s(div$);
	}
	/**
	 * Stream to the output.
	 * <p>
	 * This method is used a lot in this class and as such the short name makes
	 * the code easier to read.
	 * 
	 * @param c
	 *            Content to be streamed
	 */
	private static void s(final Object c) {
		out.println(c);
	}
	private static void sBags() {
		final Map<String, NutVersion> bags = NutFactory.the.bags;
		for (final NutVersion bag : bags.values())
			s(bag);
	}
	private static void scan(final RootDoc doc) throws InstantiationException,
			IllegalAccessException, ClassNotFoundException,
			NoSuchMethodException, InvocationTargetException {
		final ClassDoc[] clzs = doc.classes();
		scanClasses(clzs);
	}
	private static void scanClasses(final ClassDoc[] clzs)
			throws ClassNotFoundException {
		final NutFactory sys = NutFactory.the;
		for (final ClassDoc clz : clzs) {
			scanTags(clz.tags());
			final String name = clz.qualifiedName();
			final NutItem nut = sys.nut(name);
			nut.doc(clz);
			scanClasses(clz.innerClasses());
		}
	}
	private static void scanTags(final Tag[] tags) {
		for (final Tag item : tags)
			if ("@author".equals(item.name()))
				onAuthorTag(item);
	}
	private static void sFarmers() {
		s($$divClz + "NutFarmers" + $);
		final Map<String, NutAuthor> list = NutFactory.the.farmers;
		for (final NutAuthor item : list.values())
			s(item);
		s(div$);
	}
	private static void sNuts() {
		s($$divClz + "Nuts" + $);
		final Map<String, NutItem> list = NutFactory.the.nuts;
		for (final NutItem item : list.values())
			s(item);
		s(div$);
	}
	private static void sRoots() {
		s($$divClz + "Nuts" + $);
		final Map<String, NutItem> list = NutFactory.the.roots;
		for (final NutItem item : list.values())
			s(item);
		s(div$);
	}
	private static void sStatus() {
		final Map<String, NutCategory> list = NutFactory.the.categories;
		for (final NutCategory item : list.values())
			s(item);
	}
	private static void sTags(final Tag[] tags) {
		for (final Tag tag : tags) {
			s($$divClz + "NutTag" + tag.kind().replaceAll("@", "_") + $);
			s($$divClz + "NutTagName" + $);
			t(tag.name());
			s(div$);
			s($$divClz + "NutText" + $);
			sText(tag.text());
			s(div$);
			s(div$);
		}
	}
	public static boolean start(final RootDoc doc) {
		try {
			return doStart(doc) && Standard.start(doc);
		} catch (final Exception e) {
			throw new RuntimeException(e);
		}
	}
	private static void sText(String text) {
		text = escapeHtml(text);
		final String $p = $$pClz + "NutJavaDoc" + $;
		final String p$p = p$ + $p;
		text = codeHtml(text, NutHtml.$p, p$p);
		text = codeHtml(text, $br$, $br$);
		text = codeHtml(text, $b, $b);
		text = codeHtml(text, b$, b$);
		text = codeHtml(text, $i, $i);
		text = codeHtml(text, i$, i$);
		s($p);
		s(text);
		s(p$);
	}
	/**
	 * Translate and stream
	 * <p>
	 * This method is used a lot in this class and as such the short name makes
	 * the code easier to read.
	 * 
	 * @param c
	 *            Content for translation
	 */
	private static void t(final Object c) {
		s(c == null ? "null" : escapeHtml(c.toString()));
	}
}
